#include "sleep_share.h"
#include "cfg.h"
#include "dist.h"
#include "formula.h"

static uint8_t _smp_interval = 5;

void set_smp_interval(uint8_t minutes) { _smp_interval = minutes; }

uint8_t get_smp_interval() { return _smp_interval; }

#if WRIST == 1
FUNC_U64(n1, *, 3.5, double)
#else
FUNC_U64(n1, *, 5.0, double)
#endif

#define N1_MOTION_THRESHOLD _F_n1

// 仅用于v4
#define EXTRA_STAGING_TYPE()                                                   \
  return motion_avg <= DEF_EVAL(dist, N1_MOTION_THRESHOLD)() &&                \
                 steps_diff <= NREM1_REM_STEPS_DIFF_THRESHOLD                  \
             ? ENUM_SLEEP_STAGING_TYPE_NREM1                                   \
             : ENUM_SLEEP_STAGING_TYPE_REM;

void free_activities(sleep_root *root) {
  if (!root) {
    return;
  }

  for (size_t i = 0; i < root->count; i++) {
    activity_summary_t *as_obj = root->summaries + i;
    if (as_obj->cnt_acts) {
      free(as_obj->act_list);
    }
  }

  if (root->count)
    free(root->summaries);
  free(root);
}

double all_avg(const smp_hr_t *list, size_t len, double *low_avg) {
  if (!len)
    return 0;

  uint32_t *rate_list = malloc(sizeof(uint32_t) * len);
  uint32_t *low_list = malloc(sizeof(uint32_t) * len);

  for (size_t i = 0; i < len; i++) {
    rate_list[i] = (list + i)->rate;
  }
  double a = avg_uint32_t(rate_list, len);

  size_t idx = 0, j = 0;
  for (; idx < len; idx++) {
    uint32_t cur = rate_list[idx];
    if (cur < a) {
      low_list[j++] = cur;
    }
  }

  *low_avg = j > 0 ? avg_uint32_t(low_list, j) : 0;

  free(rate_list);
  free(low_list);

  return a;
}

static sleep_types_t _get_staging_type(const dist_action_t *dist,
                                       sleep_types_t last_type,
                                       const smp_hr_t *temp_list,
                                       size_t count) {
  if (ENUM_SLEEP_STAGING_TYPE_NONE == last_type) {
    return ENUM_SLEEP_STAGING_TYPE_NREM1;
  }

  /*
  跨天，步数会清空
  不能用unsigned表示
   */
  int32_t steps_diff =
      (int32_t)((temp_list + count - 1)->steps - temp_list->steps);

  uint32_t last_hr = (temp_list + count - 1)->rate;

  uint32_t *hr_list = malloc(sizeof(uint32_t) * count);
  uint32_t *motion_list = malloc(sizeof(uint32_t) * count);

  for (size_t i = 0; i < count; i++) {
    *(hr_list + i) = (temp_list + i)->rate;
    *(motion_list + i) = (temp_list + i)->motion;
  }

  double hr_sd = deviation_i32u(hr_list, count);
  double motion_avg = avg_uint32_t(motion_list, count);

  free(hr_list);
  free(motion_list);

  uint8_t all = 1;

  for (size_t i = 0; i < count; i++) {
    if ((temp_list + i)->motion > DEF_EVAL(dist, N3_MOTION_THRESHOLD)) {
      all = 0;
      break;
    }
  }

  if (all) {
    return hr_sd <= N3_HR_SD_THRESHOLD && dist->staging_type_cond(last_hr)
               ? ENUM_SLEEP_STAGING_TYPE_NREM3
               : ENUM_SLEEP_STAGING_TYPE_NREM1;
  }
  if (steps_diff < SLEEP_WAKE_STEPS_DIFF_THRESHOLD &&
      (temp_list + count - 1)->motion < SLEEP_WAKE_MOTION_THRESHOLD) {

    EXTRA_STAGING_TYPE()
  }

  return ENUM_SLEEP_STAGING_TYPE_WAKE;
}

/*
追加staging
 */
static void _append_staging_list(uint64_t sleep_start, sleep_types_t cur_type,
                                 sleep_types_t last_type,
                                 const smp_hr_t *temp_list, size_t count,
                                 linked_list_t *staging_list) {
  if (ENUM_SLEEP_STAGING_TYPE_NONE == last_type) {
    each_activity_t *s = (each_activity_t *)malloc(sizeof(each_activity_t));

    s->type = cur_type;
    s->begin = sleep_start;
    s->end = (temp_list + count - 1)->ts;
    append_item(staging_list, s, NULL);
    return;
  }

  if (count <= 1) {
    return;
  }

  if (cur_type == last_type) {
    each_activity_t *tail =
        (each_activity_t *)index_item(staging_list, -1, NULL);

    tail->type = cur_type;
    tail->begin = tail->begin;
    tail->end = (temp_list + count - 1)->ts;
  } else {
    each_activity_t *s = (each_activity_t *)malloc(sizeof(each_activity_t));

    s->type = cur_type;

    each_activity_t *tail =
        (each_activity_t *)index_item(staging_list, -1, NULL);
    s->begin = tail->end;

    s->end = (temp_list + count - 1)->ts;

    append_item(staging_list, s, NULL);
  }
}

void sleep_staging(const dist_action_t *dist, uint64_t sleep_start,
                   const smp_hr_t *slist, size_t count,
                   linked_list_t *staging_list) {
#define WINDOW_SIZE 2

  if (!count) {
    return;
  }

  if (dist->sleep_staging_min_cond(dist, sleep_start, slist, count)) {
    each_activity_t *s = (each_activity_t *)malloc(sizeof(each_activity_t));
    s->type = ENUM_SLEEP_STAGING_TYPE_NREM1;
    s->begin = sleep_start;
    s->end = (slist + count - 1)->ts;

    append_item(staging_list, s, NULL);
    return;
  }

  sleep_types_t last_type = ENUM_SLEEP_STAGING_TYPE_NONE;

  /*
  此处实现滑动窗口
  每次取2个
   */
  const smp_hr_t *window_list = NULL;

  for (size_t i = 0; i < count; i++) {
    if (count - i < WINDOW_SIZE) {
      break;
    }

    window_list = slist + i;
    sleep_types_t cur_type =
        _get_staging_type(dist, last_type, window_list, WINDOW_SIZE);
    _append_staging_list(sleep_start, cur_type, last_type, window_list,
                         WINDOW_SIZE, staging_list);
    last_type = cur_type;
  }

  if (count >= WINDOW_SIZE) {
    // 至少能滑1次
    _append_staging_list(sleep_start, ENUM_SLEEP_STAGING_TYPE_NREM1, last_type,
                         slist + count - 1, WINDOW_SIZE - 1, staging_list);
  }
}